﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using Utils;

namespace StockModel
{
    [DebuggerDisplay("Loaded Days = {_data.Count}")]
    [Serializable]
	public class QuotationCalendar : INotifyCollectionChanged, IDisposable
	{
		#region Attributes

		private readonly SortedDictionary<DateTime, QuotationDailyInfo> _data;

		#endregion

		#region Public Methods

		public QuotationCalendar()
		{
			_data = new SortedDictionary<DateTime, QuotationDailyInfo>();	
		}

		public bool IsDayCreated(DateTime day)
		{
			return _data.ContainsKey(day.Date);
		}

		public void LoadDay(DateTime day, IEnumerable<Quotation> quotations)
		{
			if (!this.IsDayCreated(day))
			{
				throw new InvalidOperationException("The day " + day + " is not created. Check its existence with IsDayLoaded() and create it with CreateDay()");
			}
			else
			{
				_data[day.Date].Clear();
				ObservableCollection2<Quotation> currentQuotations = _data[day.Date].Quotations.GetWriteLock();
				try
				{
					currentQuotations.AddRange(quotations);
				}
				finally
				{
					_data[day.Date].Quotations.FreeWriteLock();	
				}
			}
		}

		public void UnloadDay(DateTime day)
		{
			if (this.IsDayCreated(day))
			{
                QuotationDailyInfo auxQuotations = _data[day.Date];
				_data.Remove(day.Date);
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, auxQuotations));
			}
			else
			{
				throw new InvalidOperationException("The day " + day + " is not loaded. Check its existence with IsDayLoaded()");
			}
		}

		public void CreateDay(DateTime day)
		{
			QuotationDailyInfo auxQuotations = new QuotationDailyInfo(day);
			_data.Add(day.Date, auxQuotations);
			this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, auxQuotations));			
		}

		public QuotationDailyInfo GetDay(DateTime day)
		{
			if (this.IsDayCreated(day))
			{
				return _data[day.Date];
			}
			else
			{
				throw new InvalidOperationException("The day " + day + " is not loaded. Check its existence with IsDayLoaded()");
			}
		}

		public QuotationDailyInfo GetLatestAvailableDay()
		{
			if (_data.Count == 0)
			{
				throw new InvalidOperationException("There are no available days");
			}
			else
			{
				return _data[_data.Max(x => x.Key)];	
			}
		}

        public void Clear()
        {
            _data.Clear();
        }

		#endregion

		#region INotifyCollectionChanged Members

		public event NotifyCollectionChangedEventHandler CollectionChanged;

		protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs eventArgs)
		{
			if (this.CollectionChanged != null)
			{
				this.CollectionChanged(this, eventArgs);
			}
		}

		#endregion

		#region IDisposable

		protected bool Disposed { get; private set; }

		private void Dispose(bool disposing)
		{
			if (!this.Disposed)
			{
				this.InternalDispose(disposing);
			}

			this.Disposed = true;
		}

		protected virtual void InternalDispose(bool disposing)
		{
			if (disposing)
			{
				foreach (KeyValuePair<DateTime, QuotationDailyInfo> pair in _data)
				{
					pair.Value.Dispose();
				}
			}

			// Dispose here only unmanaged objects 
			// Don’t use managed objects here because maybe 
			// they have been finalized already
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		~QuotationCalendar()
		{
			this.Dispose(false);
		}

		#endregion

	}
}
